
#ifndef GLRENDERER_H
#define GLRENDERER_H

//#include <stdio.h>
#include <vector>
//#include <Windows.h>
#include <string>
#include <memory>
#include <map>


#include <GL/glew.h>

//using namespace std;

class Camera;

enum {
    POSITION_ATTRIBUTE,
    NORMAL_ATTRIBUTE,
    UV_ATTRIBUTE
};
const int NUM_ATTRIBUTES = 3;

enum {
    MVP_UNIFORM,
    MV_UNIFORM,
    COL_UNIFORM,
    DIFFUSE_SAMPLER
};
const int NUM_UNIFORMS = 4;

enum {
    CONSTANT_PROGRAM,
    SCREEN_DRAW_PROGRAM,
    MAIN_PROGRAM
};
const int NUM_PROGRAMS = 2;

enum {
    DIFFUSEMAP_SLOT,
    LIGHTMAP_SLOT
};
const int NUM_TEX_SLOTS = 2;

class Program {
    GLint gl_program_;

    GLint vtx_attributes_[NUM_ATTRIBUTES];
    GLint uniforms_[NUM_UNIFORMS];

public:

    Program(const char* vshader_file, const char* fshader_file);
    ~Program();

    inline GLint attribute(int attr) {
        return vtx_attributes_[attr];
    }

    inline GLint uniform(int uniform) {
        return uniforms_[uniform];
    }

    void Use();
};

GLuint LoadShader(GLenum shaderType, const char* pSource);

class GLTexture{
	unsigned int tex_;
public:
	GLTexture() : tex_(0){}
	GLTexture(int tex) : tex_(tex){}
	GLTexture(unsigned int tex) : tex_(tex){}
	~GLTexture(){
		glDeleteTextures(1, &tex_);
	}

	operator unsigned int(){return tex_;}
	GLTexture& operator=(unsigned int& tex){
		tex_ = tex;
		return *this;
	}
	GLTexture& operator=(unsigned int&& tex){
			tex_ = tex;
			return *this;
		}
	GLTexture& operator=(int& tex){
		tex_ = (unsigned int)tex;
		return *this;
	}
	GLTexture& operator=(int&& tex){
		tex_ = (unsigned int)tex;
		return *this;
	}
};

class GLTexture;

class RenderState {
public:
    Program* current_program;
    int binded_textures[16];
    bool wireframe_mode;
    Camera* current_cam;
};

class Renderer {
    void (*p_draw_func_)();


public:

    Renderer(int w, int h);
    ~Renderer();


    void set_DrawFunc(void (*func)());

    void Draw();


    /*************Static**************/

    static std::vector<GLuint> textures;
    static std::vector<std::string> texture_names;

    static std::map<std::string, std::weak_ptr<GLTexture>> managed_textures;

    static std::vector<Program> programs;

    static RenderState* render_state;

    static void SwitchProgram(int program);

    static void Init(RenderState* render_state);

    static void SetViewport(int start_x, int start_y, int x, int y);


    static void CheckGLError(const char* op);
    static int LoadTexture(const char* texture_name);
    static std::shared_ptr<GLTexture> LoadTexture2(const char* texture_name);
    static int LoadTGATexture(const char* texture_name);

    static std::string GetTextureName(std::shared_ptr<GLTexture> tex);
    static int GetTextureIndex(std::shared_ptr<GLTexture> tex);

    static void DeleteTexture(unsigned int tex_index);
    static void DeleteOwnTextures(unsigned int num_textures, unsigned int* tex_indexes);

    static void SwitchTexture(int slot, int tex);
    static void SwitchOwnTexture(int slot, int tex);

    //static void EnableOpenGL(HWND hWnd, HDC *hDC, HGLRC *hRC);
    //static void DisableOpenGL(HWND hWnd, HDC hDC, HGLRC hRC);

    static void SetVSync(bool sync);

    static void set_draw_zone(int start_x, int start_y, int x, int y);


};

#endif
